/*
 *  Copyright (C) 2010 {Apertum}Projects. web: www.apertum.ru email: info@apertum.ru
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ru.apertum.qsystem.client.forms;

import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jdesktop.application.Action;
import org.jdesktop.application.Application;
import org.jdesktop.application.ResourceMap;
import ru.apertum.qsystem.QSystem;
import ru.apertum.qsystem.client.Locales;
import ru.apertum.qsystem.client.QProperties;
import ru.apertum.qsystem.client.help.Helper;
import ru.apertum.qsystem.client.model.ParallelCellRenderer;
import ru.apertum.qsystem.client.model.QTray;
import ru.apertum.qsystem.client.model.QTray.MessageType;
import ru.apertum.qsystem.common.AUdpServer;
import ru.apertum.qsystem.common.Audio;
import ru.apertum.qsystem.common.CustomerState;
import ru.apertum.qsystem.common.NetCommander;
import ru.apertum.qsystem.common.QConfig;
import ru.apertum.qsystem.common.SoundPlayer;
import ru.apertum.qsystem.common.Updater;
import ru.apertum.qsystem.common.Uses;
import ru.apertum.qsystem.common.cmd.RpcGetSelfSituation;
import ru.apertum.qsystem.common.cmd.RpcGetSelfSituation.SelfService;
import ru.apertum.qsystem.common.cmd.RpcGetSelfSituation.SelfSituation;
import ru.apertum.qsystem.common.exceptions.ClientException;
import ru.apertum.qsystem.common.exceptions.QException;
import ru.apertum.qsystem.common.exceptions.ServerException;
import ru.apertum.qsystem.common.model.IClientNetProperty;
import ru.apertum.qsystem.common.model.INetProperty;
import ru.apertum.qsystem.common.model.QCustomer;
import ru.apertum.qsystem.extra.IStartClient;
import ru.apertum.qsystem.server.board.Board;
import ru.apertum.qsystem.server.model.QProperty;
import ru.apertum.qsystem.server.model.QUser;
import ru.apertum.qsystem.server.model.postponed.QPostponedList;

import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.LayoutStyle;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.border.BevelBorder;
import javax.swing.border.CompoundBorder;
import javax.swing.border.LineBorder;
import javax.swing.border.MatteBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.HeadlessException;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.ResourceBundle;
import java.util.ServiceLoader;

import static ru.apertum.qsystem.client.Locales.locMes;

/**
 * Created on 11 Сентябрь 2008 г., 16:57.
 *
 * @author Evgeniy Egorov
 */
@SuppressWarnings({"squid:S3776", "squid:S1199", "squid:S1192", "squid:S1172", "squid:S1450",
        "squid:S1604", "squid:S1161", "squid:MaximumInheritanceDepth", "squid:ClassVariableVisibilityCheck"})
@Log4j2
public final class FClient extends javax.swing.JFrame {

    /**
     * Информация для взаимодействия по сети. Формируется по данным из командной строки.
     */
    private final transient INetProperty netProperty;

    public INetProperty getNetProperty() {
        return netProperty;
    }

    /**
     * Системный трей.
     */
    private final transient QTray tray;
    /**
     * Кастомер, с которым работает юзер.
     */
    private QCustomer customer = null;

    /**
     * Устанавливаем кастомера для работы. Не может быть NULL.
     *
     * @param customer С ним работаем. Не может быть NULL.
     */
    public void setCustomer(QCustomer customer) {
        this.customer = customer;
        if (customer == null) {
            labelNextNumber.setText("");
            textAreaComments.setText("");
            return;
        }
        log.trace("Установливаем кастомера работающему клиенту и выводем его.");
        // выведем на экран некую инфу о приглашенном кастомере
        final String textCust = customer.getFullNumber();
        // Выведем номер вызванного.
        printCustomerNumber(customer.getPrefix(), customer.getNumber(), 0);

        String nextNumbers = customer.getService().getInputCaption().replaceAll("<[^>]*>", "");
        if (nextNumbers == null) {
            nextNumbers = "";
        } else {
            nextNumbers = "<u>" + nextNumbers + "</u><br>" + customer.getInputData();
        }
        labelNextNumber.setContentType("text/html");
        labelNextNumber.setText("");
        labelNextNumber.setText("<html>"
                + "<div style='text-align: center;'><span style='font-size:32.0pt;color:purple;'>" + textCust + "</span>"
                + "<span style='font-size:14.0pt;color:gray'> " + getClientPriorityName(customer.getPriority().get()) + "</span>"
                + "</div>"
                + "<div style='margin: 0px 0px 0px 2px'>"
                + "<span style='font-size:14.0pt;color:black'> " + getLocaleMessage("messages.service") + ": " + customer.getService().getName() + "</span><br>"
                + "<span style='font-size:14.0pt;color:gray'> " + nextNumbers + "</span>"
                + "</div>");
        textAreaComments.setText(customer.getTempComments());
        textAreaComments.setCaretPosition(0);
        // прикроем кнопки, которые недоступны на этом этапе работы с кастомером.
        // тут в зависимости от состояния кастомера открываем разные наборы кнопок
        switch (customer.getState()) {
            case STATE_INVITED: {
                setBlinkBoard(true);
                setKeyRegim(KEYS_INVITED);
                break;
            }
            case STATE_INVITED_SECONDARY: {
                setBlinkBoard(true);
                setKeyRegim(KEYS_INVITED);
                break;
            }
            case STATE_WORK: {
                setBlinkBoard(false);
                setKeyRegim(KEYS_STARTED);
                buttonFinish.setEnabled(customer.getService().getEnable() == 1);
                break;
            }
            case STATE_WORK_SECONDARY: {
                setBlinkBoard(false);
                setKeyRegim(KEYS_STARTED);
                buttonFinish.setEnabled(customer.getService().getEnable() == 1);
                break;
            }
            default: {
                throw new ClientException("Не известное состояние клиента \"" + customer.getState() + "\" для данного случая.");
            }
        }
        //нефиг счелкать касторами пока процесс вызывания идет при параллельном приеме
        if (user.getParallelAccess()) {
            listParallelClients.setEnabled(CustomerState.STATE_WORK.equals(customer.getState()) || CustomerState.STATE_WORK_SECONDARY.equals(customer.getState()));
        }
        // ну и разрешим параллельный вызов если есть доступ и есть кого вызывать. 
        // И если нет процесса вызывания, этот процесс должен быть завершен и не остался висеть в параллели
        if (user.getParallelAccess() && (CustomerState.STATE_WORK.equals(customer.getState()) || CustomerState.STATE_WORK_SECONDARY.equals(customer.getState()))) {
            buttonInvite.setEnabled(customersCountForUser != 0);
            menuItemInvitePostponed.setEnabled(listPostponed.getModel().getSize() != 0); // тут тоже надо, для парралельных
        }
    }

    private String getClientPriorityName(int level) {
        switch (level) {
            case 0: {
                return loc("messages.priority.low");
            }
            case 1: {
                return loc("messages.priority.standart");
            }
            case 2: {
                return loc("messages.priority.hi");
            }
            case 3: {
                return loc("messages.priority.vip");
            }
            default: {
                return loc("messages.priority.strange");
            }
        }
    }

    /**
     * Заставляем мигать все табло.
     *
     * @param blinked мигаем или нет.
     */
    private void setBlinkBoard(boolean blinked) {
        if (indicatorBoard != null) {
            indicatorBoard.printRecord(0, customer.getPrefix(), customer.getNumber(), "", "", blinked ? 0 : -1);
        }
        if (clientboardFX) {
            //not in using:  board.showData(customer.getPrefix() + customer.getNumber(), blinked)
        }
    }

    public QCustomer getCustomer() {
        return customer;
    }

    /**
     * Обозначим результат работы с клиентом если требуется.
     */
    private Long setResult() {
        Long rs = -1L;
        if (customer.getService().getResultRequired()) {
            getResults();
            Object res = JOptionPane.showInputDialog(this, getLocaleMessage("resultwork.dialog.caption"), getLocaleMessage("resultwork.dialog.title"),
                    JOptionPane.QUESTION_MESSAGE, null, getResults(), null);
            rs = res == null ? null : results.get((String) res);
        }
        return rs;
    }

    private final LinkedHashMap<String, Long> results = new LinkedHashMap<>();

    private Object[] getResults() {
        if (results.isEmpty()) {
            try {
                NetCommander.getResultsList(netProperty).stream().forEach(result -> results.put(result.getName(), result.getId()));
            } catch (QException ex) {
                throw new ClientException("Error get result: " + ex.getLocalizedMessage() + "\n", ex);
            }
        }
        return results.keySet().toArray();
    }

    /**
     * UDP Сервер. Обнаруживает изменение состояния очередей.
     */
    protected final class UDPServer extends AUdpServer {

        public UDPServer(int port) {
            super(port);
        }

        private int checkPort = 0;
        private Timer timer;
        private String pref = "";

        public void start2() {

            new Thread(this::start).start();

            try {
                Thread.sleep(500);
            } catch (InterruptedException ex) {
                log.warn("Interrupted!", ex);
                // Restore interrupted state...
                Thread.currentThread().interrupt();
            }

            if (!isActivate() && QConfig.cfg().isTerminal()) { // порт не занялся, т.к. в терминальном режиме, то нужно подсасывать мессаги из файла
                log.trace("Старт PIPE.");
                if (!new File(Uses.TEMP_FOLDER + File.separator + "pipe").exists()) {
                    new File(Uses.TEMP_FOLDER + File.separator).mkdir();
                    try (PrintWriter w = new PrintWriter(new BufferedWriter(new FileWriter(Uses.TEMP_FOLDER + File.separator + "pipe")), true)) {
                        w.println("" + new Date().getTime() + "^");
                    } catch (IOException ex) {
                        throw new ServerException(ex);
                    }
                }

                if (timer != null) {
                    timer.stop();
                }
                checkPort = 0;
                timer = new Timer(1000, (ActionEvent e) -> {
                    checkPort++;
                    try (final RandomAccessFile raf2 = new RandomAccessFile(Uses.TEMP_FOLDER + File.separator + "pipe", "r")) {
                        raf2.seek(0);
                        final String s = raf2.readLine();
                        final String[] ss = s.split("\\^");
                        if (ss.length == 2 && !pref.equals(ss[0])) {
                            pref = ss[0];
                            getData2(ss[1], null, 0);
                        }
                    } catch (IOException ex) {
                        log.error("Не прочитался pipe. ", ex);
                    }

                    if (checkPort > 180) {
                        checkPort = 0;
                        checkPort();
                    }
                });
                timer.start();
            }
        }

        private void checkPort() {
            if (timer != null) {
                timer.stop();
            }
            start2();
        }

        @Override
        protected synchronized void getData(String data, InetAddress clientAddress, int clientPort) {
            if (QConfig.cfg().isTerminal()) {
                try {
                    if (!new File(Uses.TEMP_FOLDER + File.separator + "pipe").exists()) {
                        new File(Uses.TEMP_FOLDER + File.separator).mkdir();
                        try (final PrintWriter w = new PrintWriter(new BufferedWriter(new FileWriter(Uses.TEMP_FOLDER + File.separator + "pipe")), true)) {
                            w.println("" + new Date().getTime() + "^");
                        }
                    }
                    try (final PrintWriter w = new PrintWriter(new BufferedWriter(new FileWriter(Uses.TEMP_FOLDER + File.separator + "pipe")), true)) {
                        w.println("" + new Date().getTime() + "^" + data);
                    }
                } catch (IOException ex) {
                    log.error("Не записался пакет UDP в pipe. ", ex);
                }
            }
            getData2(data, clientAddress, clientPort);
        }

        protected void getData2(String data, InetAddress clientAddress, int clientPort) {
            //Определяем, по нашей ли услуге пришел кастомер
            boolean my = false;
            for (SelfService srv : getUserPlan().getSelfservices()) {
                if (String.valueOf(srv.getId()).equals(data)) {
                    my = true;
                }
            }
            //Если кастомер встал в очередь, обрабатываемую этим юзером, то апдейтим состояние очередей.
            if (my || user.getId().toString().equals(data)) {
                //Получаем состояние очередей для юзера
                refreshSituation();
                return;
            }
            if (Uses.TASK_REFRESH_POSTPONED_POOL.equals(data)) {
                //Получаем состояние пула отложенных
                LinkedList<QCustomer> custs = null;
                try {
                    custs = NetCommander.getPostponedPoolInfo(netProperty);
                } catch (QException ex) {// вывод исключений
                    throw new ClientException(locMes("command_error") + ": " + ex.getLocalizedMessage(), ex);
                }
                LinkedList<QCustomer> rem = new LinkedList<>();
                custs.stream().filter(cust -> (cust.getIsMine() != null && !cust.getIsMine().equals(user.getId()))).forEach(rem::add);
                custs.removeAll(rem);
                listPostponed.setModel(QPostponedList.getInstance().loadPostponedList(custs));
                if (listPostponed.getModel().getSize() != 0) {
                    listPostponed.setSelectedIndex(0);
                }
                return;
            }
            if (Uses.HOW_DO_YOU_DO.equals(data)) {
                //Отправим по TCP/IP
                // NetCommander.setLive(netProperty, user.getId()); // заборонено. гы-гы. теперь жить будем по новому, даздравствует Новороссия!
            }
            if (data.startsWith("message#") && (data.startsWith("message#ALL##") || isMyMessage(data))) {
                final String mess = data.substring(data.indexOf("##") + 2);
                tray.showMessageTray(getLocaleMessage("messages.tray.information"), mess, MessageType.INFO);

                labelMessage.setText(labelMessage.getText() + "<b><span style='color:black'>" + new SimpleDateFormat("HH:mm").format(new Date())
                        + " " + getLocaleMessage("messages.tray.message") + ":</span></b><br><span style='color:blue'>" + mess.replaceAll("\n", "<br>") + "</span><br>");
            }
        }

        private boolean isMyMessage(String txt) {
            final String adr = txt.substring(0, txt.indexOf("##"));
            if (adr.contains("@" + user.getId() + "@")) {
                return true;
            }
            return getUserPlan().getSelfservices().stream().anyMatch(srv -> adr.contains("@" + srv.getId() + "@"));
        }
    }


    private final transient UDPServer udpServer;
    /**
     * Описание того, кто залогинелся.
     */
    private final QUser user;

    public QUser getUser() {
        return user;
    }

    /**
     * Описание того, сколько народу стоит в очередях к этому юзеру, ну и прочее(потом)mess Не использовать на прямую.
     * see setSituation(Element plan)
     */
    private transient SelfSituation userPlan;
    /**
     * А сколько юзеров стоит в очереди к нам.
     */
    private int customersCountForUser = 0;
    /**
     * Есть ли среди услуг услугм-рулоны.
     */
    private boolean haveRoll = false;

    public SelfSituation getUserPlan() {
        return userPlan;
    }

    private static ResourceMap localeMap = null;

    /**
     * Локализация строк.
     */
    public static String getLocaleMessage(String key) {
        if (localeMap == null) {
            localeMap = Application.getInstance(QSystem.class).getContext().getResourceMap(FClient.class);
        }
        return localeMap.getString(key);
    }

    public static String loc(String key) {
        return getLocaleMessage(key);
    }

    /**
     * Creates new form FClient.
     */
    public FClient(QUser user, final IClientNetProperty netProperty) {
        this.user = user;
        this.netProperty = netProperty;
        // инициализим trayIcon, т.к. setSituation() требует работу с tray
        final JFrame fr = this;
        tray = QTray.getInstance(QConfig.cfg().doNotHideToTray()
                ? new JFrame()
                : fr, "/ru/apertum/qsystem/client/forms/resources/client.png", getLocaleMessage("messages.tray.hint"));
        tray.addItem(getLocaleMessage("messages.tray.showClient"), (ActionEvent e) -> {
            setVisible(true);
            setState(JFrame.NORMAL);
        });
        tray.addItem("-", (ActionEvent e) -> {
        });
        tray.addItem(getLocaleMessage("messages.tray.close"), (ActionEvent e) -> {
            dispose();
            System.exit(0);
        });

        init(user, netProperty);

        // стартуем UDP сервер для обнаружения изменения состояния очередей
        udpServer = new UDPServer(netProperty.getClientPort());
        udpServer.start2();
    }

    private void init(QUser user, final IClientNetProperty netProperty) {
        addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosed(WindowEvent e) {
                // остановим UDP сервер
                udpServer.stop();
            }
        });
        initComponents();

        // выделим правым кликом
        final MouseListener ml = new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)) {
                    int selRow = treeSituation.getRowForLocation(e.getX(), e.getY());
                    TreePath selPath = treeSituation.getPathForLocation(e.getX(), e.getY());
                    treeSituation.setSelectionPath(selPath);
                    if (selRow > -1) {
                        treeSituation.setSelectionRow(selRow);
                    }
                }
            }
        };
        treeSituation.addMouseListener(ml);
        // выделили правым кликом

        btnPushToTalk.setVisible(false);
        setTitle(Uses.getLocaleMessage("project.name") + " - " + getTitle());

        try {
            setIconImage(ImageIO.read(FAdmin.class.getResource("/ru/apertum/qsystem/client/forms/resources/client.png")));
        } catch (IOException ex) {
            log.error(ex);
        }

        setPreferredSize(new Dimension(1300, 650));
        // Еслми нет разрешения вести параллельный прием, то скрыть панель вызванных
        if (!user.getParallelAccess()) {
            setSize(getPreferredSize().width - panelParallerCusts.getPreferredSize().width, getPreferredSize().height);
            panelParallerCusts.setVisible(user.getParallelAccess());
        } else {

            listParallelClients.setCellRenderer(new ParallelCellRenderer());
            listParallelClients.addListSelectionListener((ListSelectionEvent e) -> {
                final QCustomer customer1 = listParallelClients.getSelectedValue();
                if (customer1 != null && !e.getValueIsAdjusting() && (customer == null || !customer1.getId().equals(customer.getId()))) {
                    setCustomer(customer1);
                }
            });
        }

        // отрехтуем дизайн формы.
        jPanel4.setVisible(false);
        labelNextNumber.setContentType("text/html");
        labelNextNumber.setText("<html><br><p align=center><span style='font-size:24.0pt;color:black'>" + getLocaleMessage("messages.noCall") + "</span></p>");
        printCustomerNumber("", null, -1);

        if (QConfig.cfg().doNotHideToTray()) {
            this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        } else {
            // Фича. По нажатию Escape закрываем форму
            // свернем по esc
            getRootPane().registerKeyboardAction(e -> setVisible(false),
                    KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
                    JComponent.WHEN_IN_FOCUSED_WINDOW);
        }
        labelUser.setText(user.getName() + " - " + (QConfig.cfg().getPointN() != null ? QConfig.cfg().getPointN() : user.getPoint()));
        ch.setSelected(user.isPause());
        refreshSituation();

        int ii = 1;
        final ButtonGroup bg = new ButtonGroup();
        final String currLng = Locales.getInstance().getLangCurrName();
        for (String lng : Locales.getInstance().getAvailableLocales()) {
            final JRadioButtonMenuItem item = new JRadioButtonMenuItem(org.jdesktop.application.Application.getInstance(ru.apertum.qsystem.QSystem.class).getContext().getActionMap(FClient.class, this).get("setCurrentLang"));
            bg.add(item);
            item.setSelected(lng.equals(currLng));
            item.setText(lng); // NOI18N
            item.setName("RRadioButtonMenuItem" + (ii++)); // NOI18N
            menuLangs.add(item);
        }

        //привязка помощи к форме.
        final Helper helper = Helper.getHelp("ru/apertum/qsystem/client/help/client.hs");
        helper.setHelpListener(menuItemHelp);
        helper.enableHelpKey(panelDown, "client");

        ch.setOpaque(false);
        ch.addActionListener((ActionEvent e) -> {
            final boolean state1;
            try {
                state1 = NetCommander.setBussy(netProperty, getUser().getId(), ch.isSelected());
            } catch (QException ex) {// вывод исключений
                throw new ClientException(ex);
            }
            ch.setSelected(state1);
            if (state1) {
                ch.setForeground(new Color(250, 0, 0));
                ch.setText(getLocaleMessage("client.pause").toUpperCase());
            } else {
                ch.setForeground(new Color(0, 150, 0));
                ch.setText(getLocaleMessage("client.pause"));
            }
        });
        clientMenuBar.add(ch);

        Font f = new Font("Time New Roman", 0, 16);
        labelMotiv.setFont(f);
        labelMotiv.setForeground(new Color(0, 150, 0));
        clientMenuBar.add(labelMotiv);

        final Timer t = new Timer(1000, new TimerMotiv());
        t.start();

        menuItemId.setText("ID=" + user.getId());
    }

    private final JLabel labelMotiv = new JLabel();
    private final JCheckBox ch = new JCheckBox(getLocaleMessage("client.pause"));

    /**
     * Формат даты.
     */
    public final SimpleDateFormat formatHhMm = new SimpleDateFormat("HH:mm");

    class TimerMotiv implements ActionListener {

        String oldKeys = "";
        long start;
        final String prob = "                                                                                       ";
        long t1 = 5000;
        long t2 = 10000;

        @Override
        public void actionPerformed(ActionEvent e) {
            if (!oldKeys.equals(keysCurrent)) {
                oldKeys = keysCurrent;
                start = new Date().getTime();
            }
            final String action;
            switch (oldKeys) {
                case KEYS_OFF:
                    action = getLocaleMessage("no_customers");
                    t1 = 990000000;
                    t2 = 990000900;
                    break;
                case KEYS_MAY_INVITE:
                    action = getLocaleMessage("inaction");
                    t1 = 60000;
                    t2 = 120000;
                    break;
                case KEYS_INVITED:
                    action = getLocaleMessage("invited");
                    t1 = 35000;
                    t2 = 45000;
                    break;
                case KEYS_STARTED:
                    action = getLocaleMessage("in_work");
                    t1 = 300000;
                    t2 = 60000;
                    break;
                case KEYS_ALL:
                    action = "What?";
                    t1 = 1000;
                    t2 = 2000;
                    break;
                default:
                    action = "Something new";
            }
            final long tt = System.currentTimeMillis() - start;
            if (tt < t1) {
                labelMotiv.setForeground(new Color(0, 150, 0));
            } else {
                labelMotiv.setForeground(tt > t2 ? new Color(200, 0, 0) : new Color(90, 0, 230));
            }
            final String tts = prob + action + "  " + tt / 1000 / 60 + ":" + (tt / 1000) % 60;
            labelMotiv.setText(tts.substring(tts.length() - 26));
            // тут типа костылик для автообновления. удп у них видите ли не доходят! Лохи криворукие! Раз в три минуты если давно не обновлялось...
            // ок-ок. раз в 55 скеунд, ок, хрен с вами, в 25 секунд. Это обновление будет еще сессию обновлять, такая так сказать долбилка для сессии.
            if (System.currentTimeMillis() - refreshTime > 1 * 25 * 1000) {
                refreshTime = System.currentTimeMillis();
                refreshSituation();
            }
            // это скачивание и раззиповка архива раз в день.
            final String HH_MM = formatHhMm.format(new Date());
            QProperty prop = QProperties.get().getProperty(QProperties.SECTION_UPDATER, "c_download_time_HH:mm");
            if (prop != null && HH_MM.equals(prop.getValue())) {
                final QProperty property = QProperties.get().getProperty(QProperties.SECTION_UPDATER, "c_update_url");
                if (property != null) {
                    Updater.load().download(property.getValue());
                } else {
                    log.warn("No URL property for update resource: {}.c_update_url", QProperties.SECTION_UPDATER);
                }
            }
            prop = QProperties.get().getProperty(QProperties.SECTION_UPDATER, "c_unzip_time_HH:mm");
            if (prop != null && HH_MM.equals(prop.getValue())) {
                Updater.load().unzip(".");
            }
        }
    }

    /**
     * Создадим форму, спозиционируем, сконфигурируем и покажем.
     *
     * @param cfgFile файл конфигурации табло, приезжает из Dao
     */
    private static void initIndicatorBoard(final String cfgFile) {
        File f = new File(cfgFile);
        if (indicatorBoard == null && f.exists()) {

            java.awt.EventQueue.invokeLater(() -> {
                Element root = null;
                try {
                    root = new SAXReader(false).read(cfgFile).getRootElement();
                } catch (DocumentException ex) {
                    throw new ServerException("Не создали клиентское табло.", ex);
                }
                indicatorBoard = FIndicatorBoard.getIndicatorBoard(root, false);
                if (indicatorBoard != null) {
                    try {
                        indicatorBoard.setIconImage(ImageIO.read(FIndicatorBoard.class.getResource("/ru/apertum/qsystem/client/forms/resources/client.png")));
                    } catch (IOException ex) {
                        throw new ServerException(ex);
                    }
                    indicatorBoard.toPosition(QConfig.cfg().isDebug(), Integer.parseInt(root.attributeValue("x", "0")), Integer.parseInt(root.attributeValue("y", "0")));
                    indicatorBoard.setVisible(true);
                }
            });
        }
    }

    /**
     * Создадим форму, спозиционируем, сконфигурируем и покажем.
     *
     * @param cfgFile файл конфигурации табло, приезжает из Dao
     */
    private static void initIndicatorBoardJaxb(final String cfgFile) {
        File f = new File(cfgFile);
        if (indicatorBoard == null && f.exists()) {

            java.awt.EventQueue.invokeLater(() -> {
                Board board;
                try {
                    board = Board.unmarshal(new File(cfgFile));
                } catch (Exception ex) {
                    throw new ServerException("Не создали клиентское табло.", ex);
                }
                indicatorBoard = FIndicatorBoargJaxb.getIndicatorBoard(board, false);
                if (indicatorBoard != null) {
                    try {
                        indicatorBoard.setIconImage(ImageIO.read(FIndicatorBoard.class.getResource("/ru/apertum/qsystem/client/forms/resources/client.png")));
                    } catch (IOException ex) {
                        throw new ServerException(ex);
                    }
                    indicatorBoard.toPosition(QConfig.cfg().isDebug(), board.pointX, board.pointY);
                    indicatorBoard.setVisible(true);
                }
            });
        }
    }

    private static void initIndicatorBoardFX(String cfgFile) {
        File f = new File(cfgFile);
        if (!clientboardFX && f.exists()) {
            clientboardFX = true;
        }
    }

    private long refreshTime = 0;


    private static class PlanTreeNode extends DefaultMutableTreeNode {

        public final transient SelfService serv;
        public final String people;

        public PlanTreeNode(SelfService serv, String people) {
            super((serv.isFlexyInvitation() ? "*" : "") + serv.getServiceName() + ": " + serv.getCountWait() + people);
            this.serv = serv;
            this.people = people;
        }
    }

    /**
     * Определяет какова ситуация в очереди к пользователю.
     *
     * @param plan - ситуация в XML
     */
    public void setSituation(SelfSituation plan) {
        log.trace("Обновляем видимую ситуацию.");
        refreshTime = System.currentTimeMillis();
        if (plan.getSelfservices() == null) {
            return;
        }
        // На первую закладку.
        String temp = "";
        // На вторую закладку
        String tempAll = "";
        String temp1 = "";
        String color = "#cdcd32";
        int inCount = 0;

        if (plan.getExtPror() != null) {
            Uses.setExtPriorClient(plan.getExtPror());
        }

        final DefaultMutableTreeNode root = new DefaultMutableTreeNode("ROOT");
        final DefaultTreeModel tree = new DefaultTreeModel(root);
        haveRoll = false;
        // построим новую html с описанием состояния очередей
        for (SelfService serv : plan.getSelfservices()) {
            haveRoll = haveRoll || serv.isRoll(); // есть ли услуга-рулон в наличии.
            final int count = serv.getCountWait();
            final String serviceName = serv.getServiceName();

            final String people = " " + getLocaleMessage("messages.people"); // множественное
            final String mins = getLocaleMessage("fclient.min");
            if (count != 0) {
                temp = temp + "<span style='color:red'> - " + serviceName + ": " + count + people
                        + ((((count % 10) >= 2) && ((count % 10) <= 4)) ? "a" : "") + "</span><br>";
                final DefaultMutableTreeNode servNode = new PlanTreeNode(serv, people);
                root.add(servNode);
                serv.getLine().forEach(cu ->
                        servNode.add(
                                new DefaultMutableTreeNode(
                                        cu.number + (cu.waiting == null ? "" : (" " + cu.waiting + " " + mins + ""))
                                                + (cu.data == null || cu.data.isEmpty() ? "" : (" \"" + cu.data + "\"")))));
                temp1 = temp1 + " - " + serviceName + ": " + count + people
                        + ((((count % 10) >= 2) && ((count % 10) <= 4)) ? "a" : "") + "<br>";
            }
            tempAll = tempAll + "<span style='color:" + (0 == count ? "green" : "red") + "'> - " + serviceName + ": " + count + people
                    + ((((count % 10) >= 2) && ((count % 10) <= 4)) ? "a" : "") + "</span><br>";
            if (count != 0) {
                color = "#cd32cd";
            }
            inCount = inCount + count;
        }
        final String allClients = getLocaleMessage("messages.allClients") + ": ";
        labelResume.setText("<html><span style='color:" + color + "'>" + allClients + inCount + "</span>");
        temp1 = temp1 + allClients + inCount;

        // Обозначим очередь иконкой
        if (inCount == 0) {
            tray.setNewIcon("/ru/apertum/qsystem/client/forms/resources/client.png");
        } else {
            tray.setNewIcon("/ru/apertum/qsystem/client/forms/resources/clientBusy.png");
        }
        // покажим сообщение в трее если очередь была пуста и кто-то приперся
        if (customersCountForUser == 0 && inCount != 0 && plan.getCustomer() == null) {
            Toolkit.getDefaultToolkit().beep();
            if (QConfig.cfg().needSoundNotice()) {
                final String dingFilePath = QProperties.get().getProperty(QProperties.SECTION_CLIENT, "notice_sound", "/ru/apertum/qsystem/server/sound/ding.wav");
                Audio.playSound(dingFilePath);
            }
            tray.showMessageTray(Uses.getLocaleMessage("project.name"), temp1.replaceAll("<br>", "\n"), MessageType.INFO);
            if (!System.getProperty("os.name").equals("Windows 10")) {
                tray.getTrayIcon().getActionListeners()[0].actionPerformed(null);
            }
        }
        customersCountForUser = inCount;
        // посмотрим, не приехал ли кастомер, который уже вызванный
        // если приехал, то его надо учесть
        setCustomer(plan.getCustomer());
        if (plan.getCustomer() != null) {
            log.trace("От сервера приехал кастомер, который обрабатывается юзером.");
        } else {
            if (inCount == 0) {
                setKeyRegim(KEYS_OFF); //* нет клиентов, нечеого вызывать*/
            } else {
                setKeyRegim(KEYS_MAY_INVITE); //*в очереди кто-то есть, можно вызвать*/
            }
            //нефиг счелкать касторами пока процесс вызывания идет при параллельном приеме
            // но сейчас кастомера вообще нет, атк что можно щелкать
            if (user.getParallelAccess()) {
                listParallelClients.setEnabled(true);
            }

            labelNextNumber.setContentType("text/html");
            labelNextNumber.setText("<html><br><p align=center><span style='font-size:24.0pt;color:black'>" + getLocaleMessage("messages.noCall") + "</span></p>");
            printCustomerNumber("", null, -1);
            textAreaComments.setText("");
        }
        //при параллельном приеме могут быть кастомеры в работе. Отобразим их.
        if (user.getParallelAccess() && plan.getParallelList() != null) {
            listParallelClients.setModel(new DefaultComboBoxModel<>(plan.getParallelList().toArray(new QCustomer[plan.getParallelList().size()])));
            if (plan.getCustomer() != null) {
                for (QCustomer cu : plan.getParallelList()) {
                    if (plan.getCustomer().getId().equals(cu.getId())) {
                        listParallelClients.setSelectedValue(plan.getCustomer(), true);
                        break;
                    }
                }
            }
        }

        //теперь описание очередей новое
        userPlan = plan;
        treeSituation.setModel(tree);
        labelSituationAll.setText("<html>" + tempAll);

        // Ну и обновим модель для списка отложенных
        listPostponed.setModel(QPostponedList.getInstance().loadPostponedList(plan.getPostponedList()));
        LinkedList<QCustomer> custs = plan.getPostponedList();
        LinkedList<QCustomer> rem = new LinkedList<>();
        custs.stream().filter(cust -> (cust.getIsMine() != null && !cust.getIsMine().equals(user.getId()))).forEach(rem::add);
        custs.removeAll(rem);
        listPostponed.setModel(QPostponedList.getInstance().loadPostponedList(custs));
        if (listPostponed.getModel().getSize() != 0) {
            listPostponed.setSelectedIndex(0);
        }
        menuItemInvitePostponed.setEnabled(customer == null && listPostponed.getModel().getSize() != 0);
        menuItemChangeStatusPostponed.setEnabled(listPostponed.getModel().getSize() != 0);
        // ну и разрешим параллельный вызов если есть доступ и есть кого вызывать. 
        // И если нет процесса вызывания, этот процесс должен быть завершен и не остался висеть в параллели
        if (user.getParallelAccess() && customer != null
                && (CustomerState.STATE_WORK.equals(customer.getState()) || CustomerState.STATE_WORK_SECONDARY.equals(customer.getState()))) {
            menuItemInvitePostponed.setEnabled(listPostponed.getModel().getSize() != 0); // тут тоже надо, для парралельных
        }
        color = plan.getPostponedList().isEmpty() ? "#cdcd32" : "#cd32cd";
        labelPost.setText("<html><span style='color:" + color + "'>" + plan.getPostponedList().size() + "</span>");
    }

    /**
     * Возможный состояния кнопок 1 - доступна кнопка, 0 - не доступна.
     */
    public static final String KEYS_OFF = "000000";
    public static final String KEYS_ALL = "111111";
    public static final String KEYS_MAY_INVITE = "100000";
    public static final String KEYS_INVITED = "111000";
    public static final String KEYS_STARTED = "000111";
    private String keysCurrent = KEYS_OFF;

    public String getKeysCurrent() {
        return keysCurrent;
    }

    /**
     * Механизм включения/отключения кнопок.
     *
     * @param regim Конфигурация кнопок
     */
    public void setKeyRegim(String regim) {
        keysCurrent = regim;
        String reason = "";
        if (KEYS_OFF.equals(regim) && haveRoll) {
            regim = KEYS_MAY_INVITE;
            reason = " Because of user have a roll-service.";
        }
        log.trace("Buttons composition: \"{}\". {}", regim, reason);
        menuItemInvitePostponed.setEnabled((KEYS_MAY_INVITE.equals(regim) || KEYS_OFF.equals(regim)) && listPostponed.getModel().getSize() != 0);
        menuItemChangeStatusPostponed.setEnabled(listPostponed.getModel().getSize() != 0);
        buttonInvite.setEnabled('1' == regim.charAt(0));
        buttonKill.setEnabled('1' == regim.charAt(1));
        buttonStart.setEnabled('1' == regim.charAt(2));
        buttonRedirect.setEnabled('1' == regim.charAt(3));
        buttonMoveToPostponed.setEnabled('1' == regim.charAt(4));
        buttonFinish.setEnabled('1' == regim.charAt(5));

        menuItemInvite.setEnabled('1' == regim.charAt(0));
        menuItemKill.setEnabled('1' == regim.charAt(1));
        menuItemStart.setEnabled('1' == regim.charAt(2));
        menuItemRedirect.setEnabled('1' == regim.charAt(3));
        menuItemMoveToPostponed.setEnabled('1' == regim.charAt(4));
        menuItemFinish.setEnabled('1' == regim.charAt(5));
    }

    /**
     * ********************************************************************************************************.
     * /*******************************************************************************************************.
     * /************************************ Обработчики кнопок ***********************************************.
     */
    private long go() {
        log.trace("Start user action.");
        return System.currentTimeMillis();
    }

    private void end(long start) {
        log.trace("Действие завершено. Затрачено времени: {} сек.\n", ((double) (System.currentTimeMillis() - start)) / 1000);
    }

    private void extPluginIStartClientPressedButton(QUser user, INetProperty netProperty, RpcGetSelfSituation.SelfSituation situation, ActionEvent evt, int keyId) {
        // поддержка расширяемости плагинами
        for (final IStartClient event : ServiceLoader.load(IStartClient.class)) {
            log.info("Вызов SPI расширения. Описание: {}", event.getDescription());
            event.pressedButton(user, netProperty, getUserPlan(), evt, keyId);
            try {
                new Thread(() -> event.pressedButtonParallel(user, netProperty, getUserPlan(), evt, keyId)).start();
            } catch (Exception tr) {
                log.error("Вызов SPI расширения завершился ошибкой. Описание: ", tr);
            }
        }
    }

    private void extPluginIStartClientBeforePressButton(QUser user, INetProperty netProperty, RpcGetSelfSituation.SelfSituation situation, ActionEvent evt, int keyId) {
        // поддержка расширяемости плагинами
        for (final IStartClient event : ServiceLoader.load(IStartClient.class)) {
            log.info("Вызов SPI расширения. Описание: {}", event.getDescription());
            try {
                event.beforePressButton(user, netProperty, getUserPlan(), evt, keyId);
            } catch (Exception tr) {
                log.error("Вызов SPI расширения завершился ошибкой. Описание: ", tr);
            }
            try {
                new Thread(() -> event.beforePressButtonParallel(user, netProperty, getUserPlan(), evt, keyId)).start();
            } catch (Exception tr) {
                log.error("Вызов SPI расширения завершился ошибкой. Описание: ", tr);
            }
        }
    }

    private long lastInvite = 0;

    /**
     * Действие по нажатию кнопки "Вызов".
     */
    @Action
    public void inviteNextCustomer(ActionEvent evt) {
        if (System.currentTimeMillis() - lastInvite < 10_000) {
            return;
        }
        try {
            lastInvite = go();
            // поддержка расширяемости плагинами
            extPluginIStartClientBeforePressButton(user, netProperty, getUserPlan(), evt, 1);
            // Вызываем кастомера
            final QCustomer cust = NetCommander.inviteNextCustomer(netProperty, user.getId());
            if (cust != null && cust.getPostponPeriod() > 0) {
                JOptionPane.showMessageDialog(this,
                        getLocaleMessage("invite.posponed.mess.1") + " " + cust.getPostponPeriod()
                                + " " + getLocaleMessage("invite.posponed.mess.2") + " \"" + cust.getPostponedStatus() + "\".",
                        getLocaleMessage("invite.posponed.title"),
                        JOptionPane.INFORMATION_MESSAGE);
            }
            // проговорим голосом вызов с компа оператора если есть настроичка
            if (cust != null && QConfig.cfg().needVoice()) {
                final boolean isFirst = customer == null || !customer.getSemiNumber().equalsIgnoreCase(cust.getSemiNumber());
                new Thread(() ->
                        SoundPlayer.inviteClient(cust.getService(), cust, cust.getPrefix() + (cust.getNumber() < 1 ? "" : cust.getNumber()), user.getPoint(), isFirst)
                ).start();
            }

            // Показываем обстановку
            setSituation(NetCommander.getSelfServices(netProperty, user.getId()));
            // поддержка расширяемости плагинами
            extPluginIStartClientPressedButton(user, netProperty, getUserPlan(), evt, 1);
            end(lastInvite);
        } catch (HeadlessException | QException th) {
            throw new ClientException(new Exception(th));
        }
    }

    private boolean fkill = false;

    /**
     * Действие по нажатию кнопки "Отклонить".
     */
    @Action
    public void killCustomer(ActionEvent evt) {
        try {
            // поддержка расширяемости плагинами
            extPluginIStartClientBeforePressButton(user, netProperty, getUserPlan(), evt, 2);

            if (customer.getService().getExpectation() != 0 && (new Date().getTime() - customer.getStandTime().getTime()) / 1000 / 60 < customer.getService().getExpectation()) {
                if (fkill) {
                    if (JOptionPane.showConfirmDialog(this,
                            getLocaleMessage("messages.expectationl.ask") + " " + customer.getService().getExpectation() + " " + getLocaleMessage("fclient.min")
                                    + ".\n\n                                                   " + getLocaleMessage("message.expectation.chance"),
                            getLocaleMessage("messages.expectationl.caption"),
                            JOptionPane.YES_NO_OPTION,
                            JOptionPane.INFORMATION_MESSAGE) == 0) {
                        return;
                    }
                } else {
                    JOptionPane.showMessageDialog(this,
                            getLocaleMessage("messages.expectationl.ask") + " " + customer.getService().getExpectation() + " " + getLocaleMessage("fclient.min"),
                            getLocaleMessage("messages.expectationl.caption"),
                            JOptionPane.INFORMATION_MESSAGE);
                    fkill = true;
                    return;
                }
            }
            fkill = false;
            final long start = go();
            // Уточним намерения
            if (JOptionPane.showConfirmDialog(this,
                    getLocaleMessage("messages.kill.ask"),
                    getLocaleMessage("messages.kill.caption"),
                    JOptionPane.YES_NO_OPTION) == 1) {
                return;
            }
            // Убиваем пользователя
            NetCommander.killNextCustomer(netProperty, user.getId(), customer.getId());
            // Получаем новую обстановку
            //Получаем состояние очередей для юзера
            setSituation(NetCommander.getSelfServices(netProperty, user.getId()));
            // поддержка расширяемости плагинами
            extPluginIStartClientPressedButton(user, netProperty, getUserPlan(), evt, 2);
            end(start);
        } catch (HeadlessException | QException th) {
            throw new ClientException(th);
        }
    }

    /**
     * Действие по нажатию кнопки "Начать прием".
     */
    @Action
    public void getStartCustomer(ActionEvent evt) {
        try {
            final long start = go();
            // поддержка расширяемости плагинами
            extPluginIStartClientBeforePressButton(user, netProperty, getUserPlan(), evt, 3);
            // Переводим кастомера в разряд обрабатываемых
            NetCommander.getStartCustomer(netProperty, user.getId());
            // Получаем новую обстановку
            //Получаем состояние очередей для юзера
            setSituation(NetCommander.getSelfServices(netProperty, user.getId()));
            // поддержка расширяемости плагинами
            extPluginIStartClientPressedButton(user, netProperty, getUserPlan(), evt, 3);
            end(start);
        } catch (Exception th) {
            throw new ClientException(th);
        }
    }

    /**
     * Действие по нажатию кнопки "Завершить прием".
     */
    @Action
    public void getStopCustomer(ActionEvent evt) {
        try {
            // поддержка расширяемости плагинами
            extPluginIStartClientBeforePressButton(user, netProperty, getUserPlan(), evt, 6);

            //Обозначим результат если требуется
            final long start = go();
            // Переводим кастомера в разряд обрабатанных
            // это должно выкинуть кастомера нафиг, но как обработанного
            final Long res = setResult();
            if (res == null) {
                return;
            }

            String resComments = user.getName() + ": \n_______________________\n" + customer.getTempComments();
            if (customer.needBack()) {
                //Диалог ввода коментария по кастомеру если он редиректенный и нужно его вернуть
                final FRedirect dlg = FRedirect.getService(netProperty, this, customer.getTempComments(), true);
                if (dlg != null) {
                    //Если не выбрали, то выходим
                    resComments = user.getName() + ": " + dlg.getTempComments();
                }
            }
            // вернется кастомер и возможно он еще не домой а по списку услуг. Список определяется при старте кастомера в обработку специяльным юзером в регистратуре
            final QCustomer cust = NetCommander.getFinishCustomer(netProperty, user.getId(), customer.getId(), res, resComments);
            if (cust != null && cust.getService() != null && cust.getState() == CustomerState.STATE_WAIT_COMPLEX_SERVICE) {
                JOptionPane.showMessageDialog(this, "Следующая услуга" + " \"" + cust.getService().getName() + "\". "
                                + "Номер посетителя" + " \"" + cust.getFullNumber() + "\"." + "\n\n" + cust.getService().getDescription(),
                        "Продолжение комплексой услуги", JOptionPane.INFORMATION_MESSAGE);
            }
            // Получаем новую обстановку
            //Получаем состояние очередей для юзера
            setSituation(NetCommander.getSelfServices(netProperty, user.getId()));
            // поддержка расширяемости плагинами
            extPluginIStartClientPressedButton(user, netProperty, getUserPlan(), evt, 6);
            end(start);
        } catch (HeadlessException | QException th) {
            throw new ClientException(th);
        }
    }

    protected FRedirect servicesForm = null;

    /**
     * Действие по нажатию кнопки "Перенаправить".
     */
    @Action
    public void redirectCustomer(ActionEvent evt) {
        try {
            final long start = go();
            // поддержка расширяемости плагинами
            extPluginIStartClientBeforePressButton(user, netProperty, getUserPlan(), evt, 4);
            // Обозначим результат если надо
            final Long res = setResult();
            if (res == null) {
                return;
            }
            // Переводим кастомера в другую услугу
            // это должно выкинуть кастомера в другую очередь с приоритетом "переведенный"
            //Диалог выбора очереди для редиректа
            final FRedirect dlg = FRedirect.getService(netProperty, this, customer.getTempComments(), false);
            if (dlg == null) {
                //Если не выбрали, то выходим
                return;
            }

            NetCommander.redirectCustomer(netProperty, user.getId(), customer.getId(),
                    dlg.getSelectedService().getId(), dlg.getRequestBack(), user.getName() + ": " + dlg.getTempComments(), res);
            // Получаем новую обстановку
            //Получаем состояние очередей для юзера
            setSituation(NetCommander.getSelfServices(netProperty, user.getId()));
            // поддержка расширяемости плагинами
            extPluginIStartClientPressedButton(user, netProperty, getUserPlan(), evt, 4);
            end(start);
        } catch (Exception th) {
            throw new ClientException(th);
        }
    }

    //*******************************    Конец обработчиков кнопок    ***************************************************
    //*******************************************************************************************************************
    @Action
    public void showAboutBox() {
        FAbout.showAbout(this, true);
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    // Generated using JFormDesigner non-commercial license
    private void initComponents() {
        ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");
        clientMenuBar = new JMenuBar();
        fileMenu = new JMenu();
        menuLangs = new JMenu();
        menuItemRefresh = new JMenuItem();
        exitMenuItem = new JMenuItem();
        optionsMenu = new JMenu();
        menuItemFlexPriority = new JMenuItem();
        menuItemId = new JMenuItem();
        editMenu = new JMenu();
        menuItemInvite = new JMenuItem();
        menuItemKill = new JMenuItem();
        menuItemStart = new JMenuItem();
        menuItemRedirect = new JMenuItem();
        menuItemMoveToPostponed = new JMenuItem();
        menuItemFinish = new JMenuItem();
        helpMenu = new JMenu();
        menuItemHelp = new JMenuItem();
        aboutMenuItem = new JMenuItem();
        panelDown = new JPanel();
        jPanel4 = new JPanel();
        jLabel2 = new JLabel();
        jSplitPane1 = new JSplitPane();
        panelBottom = new JPanel();
        jScrollPane5 = new JScrollPane();
        textAreaComments = new JTextArea();
        btnPushToTalk = new JButton();
        jPanel1 = new JPanel();
        panelButtondBlock = new JPanel();
        buttonInvite = new JButton();
        buttonKill = new JButton();
        buttonStart = new JButton();
        buttonFinish = new JButton();
        jLabel1 = new JLabel();
        buttonRedirect = new JButton();
        buttonMoveToPostponed = new JButton();
        jScrollPane2 = new JScrollPane();
        labelNextNumber = new JTextPane();
        panelWorkplace = new JPanel();
        jLabel3 = new JLabel();
        labelUser = new JLabel();
        jLabel4 = new JLabel();
        labelResume = new JLabel();
        jTabbedPane1 = new JTabbedPane();
        jPanel5 = new JPanel();
        jScrollPane6 = new JScrollPane();
        treeSituation = new JTree();
        jPanel6 = new JPanel();
        jScrollPane3 = new JScrollPane();
        labelSituationAll = new JLabel();
        jPanel7 = new JPanel();
        jScrollPane1 = new JScrollPane();
        labelMessage = new JLabel();
        jLabel5 = new JLabel();
        labelPost = new JLabel();
        jScrollPane4 = new JScrollPane();
        listPostponed = new JList<>();
        panelParallerCusts = new JPanel();
        labelPaallelcaption = new JLabel();
        jScrollPane7 = new JScrollPane();
        listParallelClients = new JList<>();
        popupMenuPostpone = new JPopupMenu();
        menuItemInvitePostponed = new JMenuItem();
        menuItemChangeStatusPostponed = new JMenuItem();
        popupClients = new JPopupMenu();
        miFlexInvitation = new JMenuItem();
        inviteNextAction = new InviteNextAction();
        killCustomerAction = new KillCustomerAction();
        startWorkCustomerAction = new StartWorkCustomerAction();
        redirectCustomerAction = new RedirectCustomerAction();
        moveToPostponedAction = new MoveToPostponedAction();
        stopWorkCustomerAction = new StopWorkCustomerAction();
        aboutAction = new AboutAction();
        refreshAction = new RefreshAction();
        actionExit = new ExitAction();
        helpAction = new HelpAction();
        invitePostponedAction = new InvitePostponedAction();
        postponeChangeStatusAction = new PostponeChangeStatusAction();
        priorityAction = new PriorityAction();

        //======== this ========
        setTitle(bundle.getString("client.form.title"));
        setMinimumSize(new Dimension(450, 400));
        setName("Form");
        var contentPane = getContentPane();

        //======== clientMenuBar ========
        {
            clientMenuBar.setName("clientMenuBar");

            //======== fileMenu ========
            {
                fileMenu.setText(bundle.getString("client.menu.file"));
                fileMenu.setName("fileMenu");

                //======== menuLangs ========
                {
                    menuLangs.setText(bundle.getString("client.menu.langs"));
                    menuLangs.setName("menuLangs");
                }
                fileMenu.add(menuLangs);

                //---- menuItemRefresh ----
                menuItemRefresh.setName("menuItemRefresh");
                menuItemRefresh.setAction(refreshAction);
                fileMenu.add(menuItemRefresh);

                //---- exitMenuItem ----
                exitMenuItem.setName("exitMenuItem");
                exitMenuItem.setAction(actionExit);
                exitMenuItem.addActionListener(e -> exitMenuItemActionPerformed(e));
                fileMenu.add(exitMenuItem);
            }
            clientMenuBar.add(fileMenu);

            //======== optionsMenu ========
            {
                optionsMenu.setText(bundle.getString("client.menu.options"));
                optionsMenu.setName("optionsMenu");

                //---- menuItemFlexPriority ----
                menuItemFlexPriority.setName("menuItemFlexPriority");
                menuItemFlexPriority.setAction(priorityAction);
                optionsMenu.add(menuItemFlexPriority);

                //---- menuItemId ----
                menuItemId.setText(bundle.getString("client.menu.id"));
                menuItemId.setName("menuItemId");
                menuItemId.addActionListener(e -> menuItemIdActionPerformed(e));
                optionsMenu.add(menuItemId);
            }
            clientMenuBar.add(optionsMenu);

            //======== editMenu ========
            {
                editMenu.setText(bundle.getString("client.menu.edit"));
                editMenu.setName("editMenu");

                //---- menuItemInvite ----
                menuItemInvite.setName("menuItemInvite");
                menuItemInvite.setAction(inviteNextAction);
                editMenu.add(menuItemInvite);

                //---- menuItemKill ----
                menuItemKill.setName("menuItemKill");
                menuItemKill.setAction(killCustomerAction);
                editMenu.add(menuItemKill);

                //---- menuItemStart ----
                menuItemStart.setName("menuItemStart");
                menuItemStart.setAction(startWorkCustomerAction);
                editMenu.add(menuItemStart);

                //---- menuItemRedirect ----
                menuItemRedirect.setName("menuItemRedirect");
                menuItemRedirect.setAction(redirectCustomerAction);
                editMenu.add(menuItemRedirect);

                //---- menuItemMoveToPostponed ----
                menuItemMoveToPostponed.setName("menuItemMoveToPostponed");
                menuItemMoveToPostponed.setAction(moveToPostponedAction);
                editMenu.add(menuItemMoveToPostponed);

                //---- menuItemFinish ----
                menuItemFinish.setName("menuItemFinish");
                menuItemFinish.setAction(stopWorkCustomerAction);
                editMenu.add(menuItemFinish);
            }
            clientMenuBar.add(editMenu);

            //======== helpMenu ========
            {
                helpMenu.setText(bundle.getString("client.menu.about"));
                helpMenu.setName("helpMenu");

                //---- menuItemHelp ----
                menuItemHelp.setName("menuItemHelp");
                menuItemHelp.setAction(helpAction);
                helpMenu.add(menuItemHelp);

                //---- aboutMenuItem ----
                aboutMenuItem.setName("aboutMenuItem");
                aboutMenuItem.setAction(aboutAction);
                helpMenu.add(aboutMenuItem);
            }
            clientMenuBar.add(helpMenu);
        }
        setJMenuBar(clientMenuBar);

        //======== panelDown ========
        {
            panelDown.setBorder(new MatteBorder(1, 1, 1, 1, Color.black));
            panelDown.setName("panelDown");

            //======== jPanel4 ========
            {
                jPanel4.setBorder(new MatteBorder(1, 1, 1, 1, Color.black));
                jPanel4.setName("jPanel4");

                //---- jLabel2 ----
                jLabel2.setText("no-txt");
                jLabel2.setName("jLabel2");

                GroupLayout jPanel4Layout = new GroupLayout(jPanel4);
                jPanel4.setLayout(jPanel4Layout);
                jPanel4Layout.setHorizontalGroup(
                    jPanel4Layout.createParallelGroup()
                        .addGroup(jPanel4Layout.createSequentialGroup()
                            .addContainerGap()
                            .addComponent(jLabel2, GroupLayout.PREFERRED_SIZE, 355, GroupLayout.PREFERRED_SIZE)
                            .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                );
                jPanel4Layout.setVerticalGroup(
                    jPanel4Layout.createParallelGroup()
                        .addComponent(jLabel2, GroupLayout.DEFAULT_SIZE, 53, Short.MAX_VALUE)
                );
            }

            //======== jSplitPane1 ========
            {
                jSplitPane1.setBorder(null);
                jSplitPane1.setDividerLocation(410);
                jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT);
                jSplitPane1.setContinuousLayout(true);
                jSplitPane1.setName("jSplitPane1");
                jSplitPane1.setOpaque(false);

                //======== panelBottom ========
                {
                    panelBottom.setBorder(new TitledBorder(bundle.getString("client.comments.title")));
                    panelBottom.setName("panelBottom");
                    panelBottom.setOpaque(false);

                    //======== jScrollPane5 ========
                    {
                        jScrollPane5.setName("jScrollPane5");

                        //---- textAreaComments ----
                        textAreaComments.setEditable(false);
                        textAreaComments.setColumns(20);
                        textAreaComments.setLineWrap(true);
                        textAreaComments.setRows(5);
                        textAreaComments.setWrapStyleWord(true);
                        textAreaComments.setBorder(null);
                        textAreaComments.setName("textAreaComments");
                        jScrollPane5.setViewportView(textAreaComments);
                    }

                    //---- btnPushToTalk ----
                    btnPushToTalk.setText(bundle.getString("client.btnPushToTalk.text"));
                    btnPushToTalk.setToolTipText(bundle.getString("client.btnPushToTalk.toolTipText"));
                    btnPushToTalk.setBorder(new CompoundBorder(
                        new BevelBorder(BevelBorder.RAISED),
                        new BevelBorder(BevelBorder.RAISED)));
                    btnPushToTalk.setName("btnPushToTalk");

                    GroupLayout panelBottomLayout = new GroupLayout(panelBottom);
                    panelBottom.setLayout(panelBottomLayout);
                    panelBottomLayout.setHorizontalGroup(
                        panelBottomLayout.createParallelGroup()
                            .addGroup(panelBottomLayout.createSequentialGroup()
                                .addComponent(jScrollPane5, GroupLayout.DEFAULT_SIZE, 868, Short.MAX_VALUE)
                                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(btnPushToTalk, GroupLayout.PREFERRED_SIZE, 72, GroupLayout.PREFERRED_SIZE))
                    );
                    panelBottomLayout.setVerticalGroup(
                        panelBottomLayout.createParallelGroup()
                            .addComponent(jScrollPane5, GroupLayout.DEFAULT_SIZE, 95, Short.MAX_VALUE)
                            .addComponent(btnPushToTalk, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    );
                }
                jSplitPane1.add(panelBottom, "bottom");

                //======== jPanel1 ========
                {
                    jPanel1.setName("jPanel1");
                    jPanel1.setOpaque(false);

                    //======== panelButtondBlock ========
                    {
                        panelButtondBlock.setBorder(new MatteBorder(1, 1, 1, 1, Color.black));
                        panelButtondBlock.setName("jPanel3");
                        panelButtondBlock.setOpaque(false);

                        //---- buttonInvite ----
                        buttonInvite.setName("buttonInvite");
                        buttonInvite.setAction(inviteNextAction);

                        //---- buttonKill ----
                        buttonKill.setName("buttonKill");
                        buttonKill.setAction(killCustomerAction);

                        //---- buttonStart ----
                        buttonStart.setName("buttonStart");
                        buttonStart.setAction(startWorkCustomerAction);

                        //---- buttonFinish ----
                        buttonFinish.setName("buttonFinish");
                        buttonFinish.setAction(stopWorkCustomerAction);
                        buttonFinish.addActionListener(e -> getStopCustomer(e));

                        //---- jLabel1 ----
                        jLabel1.setText(bundle.getString("client.next-number"));
                        jLabel1.setName("jLabel1");

                        //---- buttonRedirect ----
                        buttonRedirect.setName("buttonRedirect");
                        buttonRedirect.setAction(redirectCustomerAction);

                        //---- buttonMoveToPostponed ----
                        buttonMoveToPostponed.setName("buttonMoveToPostponed");
                        buttonMoveToPostponed.setAction(moveToPostponedAction);

                        //======== jScrollPane2 ========
                        {
                            jScrollPane2.setBorder(new MatteBorder(1, 1, 1, 1, Color.black));
                            jScrollPane2.setName("jScrollPane2");
                            jScrollPane2.setOpaque(false);

                            //---- labelNextNumber ----
                            labelNextNumber.setEditable(false);
                            labelNextNumber.setBorder(new LineBorder(Color.black, 2));
                            labelNextNumber.setContentType("text/html");
                            labelNextNumber.setName("labelNextNumber");
                            jScrollPane2.setViewportView(labelNextNumber);
                        }

                        GroupLayout panelButtondBlockLayout = new GroupLayout(panelButtondBlock);
                        panelButtondBlock.setLayout(panelButtondBlockLayout);
                        panelButtondBlockLayout.setHorizontalGroup(
                            panelButtondBlockLayout.createParallelGroup()
                                .addGroup(panelButtondBlockLayout.createSequentialGroup()
                                    .addContainerGap()
                                    .addGroup(panelButtondBlockLayout.createParallelGroup()
                                        .addComponent(buttonInvite, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                        .addComponent(buttonKill, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                        .addComponent(buttonStart, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 216, Short.MAX_VALUE)
                                        .addComponent(buttonRedirect, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                        .addComponent(buttonMoveToPostponed, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                        .addComponent(buttonFinish, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                        .addComponent(jLabel1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                        .addComponent(jScrollPane2, GroupLayout.Alignment.TRAILING))
                                    .addContainerGap())
                        );
                        panelButtondBlockLayout.setVerticalGroup(
                            panelButtondBlockLayout.createParallelGroup()
                                .addGroup(panelButtondBlockLayout.createSequentialGroup()
                                    .addContainerGap()
                                    .addComponent(buttonInvite)
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(buttonKill)
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(buttonStart)
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(buttonRedirect)
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(buttonMoveToPostponed)
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(buttonFinish)
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(jLabel1)
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(jScrollPane2)
                                    .addContainerGap())
                        );
                    }

                    //======== panelWorkplace ========
                    {
                        panelWorkplace.setBorder(new MatteBorder(1, 1, 1, 1, Color.black));
                        panelWorkplace.setName("jPanel2");
                        panelWorkplace.setOpaque(false);

                        //---- jLabel3 ----
                        jLabel3.setText(bundle.getString("client.user"));
                        jLabel3.setName("jLabel3");

                        //---- labelUser ----
                        labelUser.setText("no-txt");
                        labelUser.setName("labelUser");

                        //---- jLabel4 ----
                        jLabel4.setIcon(new ImageIcon(getClass().getResource("/ru/apertum/qsystem/client/forms/resources/qiui.png")));
                        jLabel4.setText(bundle.getString("client.situation-srv"));
                        jLabel4.setName("jLabel4");

                        //---- labelResume ----
                        labelResume.setText("no-txt");
                        labelResume.setName("labelResume");

                        //======== jTabbedPane1 ========
                        {
                            jTabbedPane1.setName("jTabbedPane1");

                            //======== jPanel5 ========
                            {
                                jPanel5.setName("jPanel5");
                                jPanel5.setOpaque(false);

                                //======== jScrollPane6 ========
                                {
                                    jScrollPane6.setBorder(new MatteBorder(1, 1, 1, 1, Color.black));
                                    jScrollPane6.setName("jScrollPane6");

                                    //---- treeSituation ----
                                    treeSituation.setAutoscrolls(true);
                                    treeSituation.setComponentPopupMenu(popupClients);
                                    treeSituation.setExpandsSelectedPaths(false);
                                    treeSituation.setName("treeSituation");
                                    treeSituation.setRootVisible(false);
                                    treeSituation.setShowsRootHandles(true);
                                    jScrollPane6.setViewportView(treeSituation);
                                }

                                GroupLayout jPanel5Layout = new GroupLayout(jPanel5);
                                jPanel5.setLayout(jPanel5Layout);
                                jPanel5Layout.setHorizontalGroup(
                                    jPanel5Layout.createParallelGroup()
                                        .addComponent(jScrollPane6, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 706, Short.MAX_VALUE)
                                );
                                jPanel5Layout.setVerticalGroup(
                                    jPanel5Layout.createParallelGroup()
                                        .addComponent(jScrollPane6, GroupLayout.DEFAULT_SIZE, 157, Short.MAX_VALUE)
                                );
                            }
                            jTabbedPane1.addTab(bundle.getString("client.tab-waits"), jPanel5);

                            //======== jPanel6 ========
                            {
                                jPanel6.setName("jPanel6");
                                jPanel6.setOpaque(false);

                                //======== jScrollPane3 ========
                                {
                                    jScrollPane3.setBorder(new MatteBorder(1, 1, 1, 1, Color.black));
                                    jScrollPane3.setName("jScrollPane3");
                                    jScrollPane3.setOpaque(false);

                                    //---- labelSituationAll ----
                                    labelSituationAll.setText("def-txt");
                                    labelSituationAll.setVerticalAlignment(SwingConstants.TOP);
                                    labelSituationAll.setName("labelSituationAll");
                                    jScrollPane3.setViewportView(labelSituationAll);
                                }

                                GroupLayout jPanel6Layout = new GroupLayout(jPanel6);
                                jPanel6.setLayout(jPanel6Layout);
                                jPanel6Layout.setHorizontalGroup(
                                    jPanel6Layout.createParallelGroup()
                                        .addComponent(jScrollPane3, GroupLayout.DEFAULT_SIZE, 706, Short.MAX_VALUE)
                                );
                                jPanel6Layout.setVerticalGroup(
                                    jPanel6Layout.createParallelGroup()
                                        .addComponent(jScrollPane3, GroupLayout.DEFAULT_SIZE, 157, Short.MAX_VALUE)
                                );
                            }
                            jTabbedPane1.addTab(bundle.getString("client.tab-all-srv"), jPanel6);

                            //======== jPanel7 ========
                            {
                                jPanel7.setName("jPanel7");

                                //======== jScrollPane1 ========
                                {
                                    jScrollPane1.setBorder(new MatteBorder(1, 1, 1, 1, Color.black));
                                    jScrollPane1.setName("jScrollPane1");

                                    //---- labelMessage ----
                                    labelMessage.setText(bundle.getString("client.label-message"));
                                    labelMessage.setVerticalAlignment(SwingConstants.TOP);
                                    labelMessage.setName("labelMessage");
                                    jScrollPane1.setViewportView(labelMessage);
                                }

                                GroupLayout jPanel7Layout = new GroupLayout(jPanel7);
                                jPanel7.setLayout(jPanel7Layout);
                                jPanel7Layout.setHorizontalGroup(
                                    jPanel7Layout.createParallelGroup()
                                        .addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 706, Short.MAX_VALUE)
                                );
                                jPanel7Layout.setVerticalGroup(
                                    jPanel7Layout.createParallelGroup()
                                        .addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 157, Short.MAX_VALUE)
                                );
                            }
                            jTabbedPane1.addTab(bundle.getString("client.tab-messages"), jPanel7);
                        }

                        //---- jLabel5 ----
                        jLabel5.setIcon(new ImageIcon(getClass().getResource("/ru/apertum/qsystem/client/forms/resources/postpon.png")));
                        jLabel5.setText(bundle.getString("client.postponed"));
                        jLabel5.setName("jLabel5");

                        //---- labelPost ----
                        labelPost.setName("labelPost");
                        labelPost.setText("no-txt");

                        //======== jScrollPane4 ========
                        {
                            jScrollPane4.setBorder(new MatteBorder(1, 1, 1, 1, Color.black));
                            jScrollPane4.setName("jScrollPane4");

                            //---- listPostponed ----
                            listPostponed.setBorder(new LineBorder(Color.black, 2));
                            listPostponed.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                            listPostponed.setComponentPopupMenu(popupMenuPostpone);
                            listPostponed.setName("listPostponed");
                            jScrollPane4.setViewportView(listPostponed);
                        }

                        GroupLayout panelWorkplaceLayout = new GroupLayout(panelWorkplace);
                        panelWorkplace.setLayout(panelWorkplaceLayout);
                        panelWorkplaceLayout.setHorizontalGroup(
                            panelWorkplaceLayout.createParallelGroup()
                                .addGroup(GroupLayout.Alignment.TRAILING, panelWorkplaceLayout.createSequentialGroup()
                                    .addContainerGap()
                                    .addGroup(panelWorkplaceLayout.createParallelGroup(GroupLayout.Alignment.TRAILING)
                                        .addComponent(jScrollPane4, GroupLayout.Alignment.LEADING)
                                        .addComponent(jTabbedPane1)
                                        .addGroup(GroupLayout.Alignment.LEADING, panelWorkplaceLayout.createSequentialGroup()
                                            .addComponent(jLabel5)
                                            .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
                                            .addComponent(labelPost))
                                        .addGroup(GroupLayout.Alignment.LEADING, panelWorkplaceLayout.createSequentialGroup()
                                            .addComponent(jLabel3)
                                            .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                            .addComponent(labelUser, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                                        .addGroup(panelWorkplaceLayout.createSequentialGroup()
                                            .addComponent(jLabel4)
                                            .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
                                            .addComponent(labelResume, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
                                    .addContainerGap())
                        );
                        panelWorkplaceLayout.setVerticalGroup(
                            panelWorkplaceLayout.createParallelGroup()
                                .addGroup(panelWorkplaceLayout.createSequentialGroup()
                                    .addContainerGap()
                                    .addGroup(panelWorkplaceLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                        .addComponent(jLabel3)
                                        .addComponent(labelUser))
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addGroup(panelWorkplaceLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                        .addComponent(jLabel4)
                                        .addComponent(labelResume))
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(jTabbedPane1)
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addGroup(panelWorkplaceLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                        .addComponent(jLabel5)
                                        .addComponent(labelPost))
                                    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(jScrollPane4, GroupLayout.PREFERRED_SIZE, 119, GroupLayout.PREFERRED_SIZE)
                                    .addContainerGap())
                        );
                    }

                    GroupLayout jPanel1Layout = new GroupLayout(jPanel1);
                    jPanel1.setLayout(jPanel1Layout);
                    jPanel1Layout.setHorizontalGroup(
                        jPanel1Layout.createParallelGroup()
                            .addGroup(GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
                                .addComponent(panelWorkplace, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(panelButtondBlock, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
                    );
                    jPanel1Layout.setVerticalGroup(
                        jPanel1Layout.createParallelGroup()
                            .addComponent(panelButtondBlock, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(panelWorkplace, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    );
                }
                jSplitPane1.setTopComponent(jPanel1);
            }

            GroupLayout panelDownLayout = new GroupLayout(panelDown);
            panelDown.setLayout(panelDownLayout);
            panelDownLayout.setHorizontalGroup(
                panelDownLayout.createParallelGroup()
                    .addComponent(jPanel4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jSplitPane1, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 0, Short.MAX_VALUE)
            );
            panelDownLayout.setVerticalGroup(
                panelDownLayout.createParallelGroup()
                    .addGroup(panelDownLayout.createSequentialGroup()
                        .addComponent(jPanel4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jSplitPane1))
            );
        }

        //======== panelParallerCusts ========
        {
            panelParallerCusts.setBorder(new LineBorder(Color.black, 1, true));
            panelParallerCusts.setName("panelParallerCusts");

            //---- labelPaallelcaption ----
            labelPaallelcaption.setHorizontalAlignment(SwingConstants.CENTER);
            labelPaallelcaption.setText(bundle.getString("client.label-parallel-caption"));
            labelPaallelcaption.setName("labelPaallelcaption");

            //======== jScrollPane7 ========
            {
                jScrollPane7.setBorder(new MatteBorder(1, 1, 1, 1, Color.black));
                jScrollPane7.setName("jScrollPane7");

                //---- listParallelClients ----
                listParallelClients.setBorder(new LineBorder(Color.black, 5));
                listParallelClients.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                listParallelClients.setName("listParallelClients");
                jScrollPane7.setViewportView(listParallelClients);
            }

            GroupLayout panelParallerCustsLayout = new GroupLayout(panelParallerCusts);
            panelParallerCusts.setLayout(panelParallerCustsLayout);
            panelParallerCustsLayout.setHorizontalGroup(
                panelParallerCustsLayout.createParallelGroup()
                    .addGroup(GroupLayout.Alignment.TRAILING, panelParallerCustsLayout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(panelParallerCustsLayout.createParallelGroup(GroupLayout.Alignment.TRAILING)
                            .addComponent(jScrollPane7)
                            .addComponent(labelPaallelcaption, GroupLayout.DEFAULT_SIZE, 356, Short.MAX_VALUE))
                        .addContainerGap())
            );
            panelParallerCustsLayout.setVerticalGroup(
                panelParallerCustsLayout.createParallelGroup()
                    .addGroup(panelParallerCustsLayout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(labelPaallelcaption)
                        .addGap(18, 18, 18)
                        .addComponent(jScrollPane7)
                        .addContainerGap())
            );
        }

        GroupLayout contentPaneLayout = new GroupLayout(contentPane);
        contentPane.setLayout(contentPaneLayout);
        contentPaneLayout.setHorizontalGroup(
            contentPaneLayout.createParallelGroup()
                .addGroup(contentPaneLayout.createSequentialGroup()
                    .addComponent(panelDown, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGap(0, 0, 0)
                    .addComponent(panelParallerCusts, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
        );
        contentPaneLayout.setVerticalGroup(
            contentPaneLayout.createParallelGroup()
                .addComponent(panelDown, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(panelParallerCusts, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        setSize(1330, 655);
        setLocationRelativeTo(null);

        //======== popupMenuPostpone ========
        {
            popupMenuPostpone.setName("popupMenuPostpone");

            //---- menuItemInvitePostponed ----
            menuItemInvitePostponed.setName("menuItemInvitePostponed");
            menuItemInvitePostponed.setAction(invitePostponedAction);
            menuItemInvitePostponed.setText(bundle.getString("client.event.invite-postponed"));
            menuItemInvitePostponed.setToolTipText(bundle.getString("client.event.invite-postponed.tip"));
            popupMenuPostpone.add(menuItemInvitePostponed);

            //---- menuItemChangeStatusPostponed ----
            menuItemChangeStatusPostponed.setName("menuItemChangeStatusPostponed");
            menuItemChangeStatusPostponed.setAction(postponeChangeStatusAction);
            menuItemChangeStatusPostponed.setText(bundle.getString("client.event.change-stutas-postponed"));
            menuItemChangeStatusPostponed.setToolTipText(bundle.getString("client.event.change-stutas-postponed.tip"));
            popupMenuPostpone.add(menuItemChangeStatusPostponed);
        }

        //======== popupClients ========
        {
            popupClients.setName("popupClients");
            popupClients.addPopupMenuListener(new PopupMenuListener() {
                @Override
                public void popupMenuCanceled(PopupMenuEvent e) {}
                @Override
                public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {}
                @Override
                public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
                    popupClientsPopupMenuWillBecomeVisible(e);
                }
            });

            //---- miFlexInvitation ----
            miFlexInvitation.setText(bundle.getString("client.event.direct-invite"));
            miFlexInvitation.setName("miFlexInvitation");
            miFlexInvitation.setToolTipText(bundle.getString("client.event.direct-invite.tip"));
            miFlexInvitation.addActionListener(e -> miFlexInvitationActionPerformed(e));
            popupClients.add(miFlexInvitation);
        }
    }// </editor-fold>//GEN-END:initComponents

    private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitMenuItemActionPerformed
        System.exit(0);
    }//GEN-LAST:event_exitMenuItemActionPerformed

    private void menuItemIdActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_menuItemIdActionPerformed
        if (JOptionPane.showInputDialog(this, "User ID from database:", "ID", 1, null, null, user.getId().toString()) != null) {
            final StringSelection stringSelection = new StringSelection(user.getId().toString());
            final Clipboard clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard();
            clpbrd.setContents(stringSelection, null);
        }
    }//GEN-LAST:event_menuItemIdActionPerformed

    private void miFlexInvitationActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_miFlexInvitationActionPerformed
        final TreePath selectedPath = treeSituation.getSelectionPath();
        if (selectedPath != null && selectedPath.getLastPathComponent() instanceof PlanTreeNode) {
            final PlanTreeNode node = (PlanTreeNode) selectedPath.getLastPathComponent();
            if (node.serv.isFlexyInvitation()
                    && (getKeysCurrent().equals(FClient.KEYS_MAY_INVITE) || getKeysCurrent().equals(FClient.KEYS_OFF)
                    //незабываем про возможность параллельного приема.
                    || (user.getParallelAccess() && customersCountForUser != 0 && getKeysCurrent().equals(FClient.KEYS_STARTED)))) {
                inviteFlexCustomer(node);
            }
        }
    }//GEN-LAST:event_miFlexInvitationActionPerformed

    private void popupClientsPopupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {//GEN-FIRST:event_popupClientsPopupMenuWillBecomeVisible
        boolean flag = false;
        final TreePath selectedPath = treeSituation.getSelectionPath();
        if (selectedPath != null && selectedPath.getLastPathComponent() instanceof PlanTreeNode) {
            final PlanTreeNode node = (PlanTreeNode) selectedPath.getLastPathComponent();
            flag = node.serv.isFlexyInvitation();
        }
        miFlexInvitation.setEnabled(flag);
        miFlexInvitation.setVisible(flag);
    }//GEN-LAST:event_popupClientsPopupMenuWillBecomeVisible

    private void inviteFlexCustomer(PlanTreeNode node) {
        try {
            lastInvite = go();
            // поддержка расширяемости плагинами
            extPluginIStartClientBeforePressButton(user, netProperty, getUserPlan(), new ActionEvent(node, 1, "inviteFlexCustomer"), 1);
            // Вызываем кастомера
            final QCustomer cust = NetCommander.inviteNextCustomer(netProperty, user.getId(), node.serv.getId());
            if (cust != null && cust.getPostponPeriod() > 0) {
                JOptionPane.showMessageDialog(this,
                        getLocaleMessage("invite.posponed.mess.1") + " " + cust.getPostponPeriod() + " "
                                + getLocaleMessage("invite.posponed.mess.2") + " \"" + cust.getPostponedStatus() + "\".",
                        getLocaleMessage("invite.posponed.title"),
                        JOptionPane.INFORMATION_MESSAGE);
            }
            // Показываем обстановку
            setSituation(NetCommander.getSelfServices(netProperty, user.getId()));
            // поддержка расширяемости плагинами
            extPluginIStartClientPressedButton(user, netProperty, getUserPlan(), new ActionEvent(node, 1, "inviteFlexCustomer"), 1);
            end(lastInvite);
        } catch (HeadlessException | QException th) {
            throw new ClientException(th);
        }
    }

    /**
     * Табло вывода кастомера.
     */
    private static FIndicatorBoard indicatorBoard = null;
    private static boolean clientboardFX = false;

    /**
     * Если есть монитор для вывода номера клиента, то выведет текст нома на него. Если текст для вывода пуст, то тобло перестанет моргать и отчистиццо.
     *
     * @param pref       текст нома клиента.
     * @param num        текст нома клиента.
     * @param blinkCount 0 - постоянное мигание, -1 не мигает. число - количество миганий
     */
    private void printCustomerNumber(String pref, Integer num, int blinkCount) {
        if (indicatorBoard != null) {
            indicatorBoard.printRecord(0, pref, num, "", "", blinkCount);
        }
        if (clientboardFX) {
            //not in using: board.showData("", false)
        }
    }

    /**
     * Приложение пульта оператора.
     */
    public static void main() {
        Uses.startSplashClient();
        // Загрузка плагинов из папки plugins
        if (QConfig.cfg().isPlaginable()) {
            Uses.loadPlugins("./plugins/");
        }

        //Загрузим серверные параметры
        boolean flag = false;
        try {
            QProperties.get().load(QConfig.cfg().getClientNetProperty());
        } catch (QException e) {
            flag = true;
            throw new ClientException(e);
        } finally {
            if (flag) {
                System.exit(1);
            }
        }

        if (!QConfig.cfg().isTerminal()) {// в терминальном режиме запускаем много копий
            // Отсечем вторую копию.
            try {
                final DatagramSocket socket = new DatagramSocket(QConfig.cfg().getClientPort());
                socket.close();
            } catch (SocketException ex) {
                log.error("Сервер UDP не запустился, вторая копия не позволяется.");
                JOptionPane.showMessageDialog(null, getLocaleMessage("messages.restart.mess"), getLocaleMessage("messages.restart.caption"), JOptionPane.INFORMATION_MESSAGE);
                System.exit(0);
            }
        }
        // Определим кто работает на данном месте.
        final QUser user = FLogin.logining(QConfig.cfg().getClientNetProperty(), null, true, 3, FLogin.LEVEL_USER);
        Uses.showSplash();
        try {
            //Определим, надо ли выводить кастомера на второй экран.
            // Для этого должны быть переданы две координаты для определения этого монитора
            // -posx x -posy y
            if (new File(QConfig.cfg().getBoardCfgFile()).exists()) {
                if (QConfig.cfg().getBoardCfgFile().contains("old")) {
                    initIndicatorBoard(QConfig.cfg().getBoardCfgFile());
                } else {
                    initIndicatorBoardJaxb(QConfig.cfg().getBoardCfgFile());
                }
            } else {
                if (new File(QConfig.cfg().getBoardCfgFXfile()).exists()) {
                    initIndicatorBoardFX(QConfig.cfg().getBoardCfgFXfile());
                }
            }

            // Посмотрим, не пытались ли влезть под уже имеющейся в системе ролью
            if (!NetCommander.getSelfServicesCheck(QConfig.cfg().getClientNetProperty(), user.getId())) {
                Uses.closeSplash();
                JOptionPane.showMessageDialog(null, getLocaleMessage("messages.stop.access_denay.mess"), getLocaleMessage("messages.stop.mess"), JOptionPane.INFORMATION_MESSAGE);
                System.exit(0);
            }
            //Показываем форму и передаем в нее описание того кто залогинился
            fClient = new FClient(user, QConfig.cfg().getClientNetProperty());
            // подключения плагинов, которые стартуют в самом начале.
            // поддержка расширяемости плагинами
            for (final IStartClient event : ServiceLoader.load(IStartClient.class)) {
                log.info("Invoke SPI extension. Description: {}", event.getDescription());
                new Thread(() -> event.start(fClient)).start();
            }
            fClient.setVisible(true);
        } catch (Exception ex) {
            log.error("Ошибка при старте: ", ex);
            Uses.closeSplash();
            System.exit(0);
        } finally {
            Uses.closeSplash();
        }
    }

    private static FClient fClient;

    public static FClient getForm() {
        return fClient;
    }

    @Action
    public void refreshClient() {
        //Получаем состояние очередей для юзера
        refreshSituation(true);
    }

    public void refreshSituation() {
        refreshSituation(null);
    }

    /**
     * Обновить состояние, запросив с сервера.
     *
     * @param forced Обновлять всю инфу несмотря что недавно обновляли.
     */
    public void refreshSituation(Boolean forced) {
        //Получаем состояние очередей для юзера
        try {
            setSituation(NetCommander.getSelfServices(netProperty, user.getId(), forced));
            spd = -1;
        } catch (QException th) {
            spd++;
            log.error("Ошибка при обновлении состояния.");
            if (spd % 20 == 0) {
                tray.showMessageTray(getLocaleMessage("qsys_server_warning"), loc("qsys_server_down"), MessageType.WARNING);
            }
        } catch (Exception th) {
            throw new ClientException(new Exception(th));
        }
    }

    private int spd = -1;

    @Action
    public void help() {
        //not in using
    }

    /**
     * Выставим текущую локаль.
     */
    @Action
    public void setCurrentLang() {
        for (int i = 0; i < menuLangs.getItemCount(); i++) {
            if (((JRadioButtonMenuItem) menuLangs.getItem(i)).isSelected()) {
                Locales.getInstance().setLangCurrent(((JRadioButtonMenuItem) menuLangs.getItem(i)).getText());
            }
        }
    }

    /**
     * Отправим в отложенные.
     */
    @Action
    private void moveToPostponed(ActionEvent e) {
        try {
            final long start = go();
            // поддержка расширяемости плагинами
            extPluginIStartClientBeforePressButton(user, netProperty, getUserPlan(),
                    new ActionEvent(buttonMoveToPostponed, 13, buttonMoveToPostponed.getActionCommand(), System.currentTimeMillis(), 1), 5);

            if (moveToPostponed == null) {
                moveToPostponed = new FMoveToPostponed(fClient, true, getResults());
            }
            Uses.setLocation(moveToPostponed);
            moveToPostponed.setVisible(true);
            if (!moveToPostponed.isOK()) {
                return;
            }
            NetCommander.customerToPostpone(netProperty, user.getId(), customer.getId(), moveToPostponed.getResult(), moveToPostponed.getPeriod(), moveToPostponed.isMine());
            // Показываем обстановку
            setSituation(NetCommander.getSelfServices(netProperty, user.getId()));
            // поддержка расширяемости плагинами
            extPluginIStartClientPressedButton(user, netProperty, getUserPlan(),
                    new ActionEvent(buttonMoveToPostponed, 13, buttonMoveToPostponed.getActionCommand(), System.currentTimeMillis(), 1), 5);
            end(start);
        } catch (Exception th) {
            throw new ClientException(th);
        }
    }

    private FMoveToPostponed moveToPostponed;

    /**
     * Вызовем отложенного.
     */
    @Action
    public void invitePostponed() throws QException {
        if (listPostponed.getSelectedIndex() != -1) {
            if (customer == null
                    || (user.getParallelAccess()
                    && (CustomerState.STATE_WORK.equals(customer.getState()) || CustomerState.STATE_WORK_SECONDARY.equals(customer.getState())))) {
                final long start = go();
                // поддержка расширяемости плагинами
                extPluginIStartClientBeforePressButton(user, netProperty, getUserPlan(), new ActionEvent(menuItemInvitePostponed, 1, KEYS_MAY_INVITE), 1);

                final QCustomer cust = (QCustomer) listPostponed.getSelectedValue();
                NetCommander.invitePostponeCustomer(netProperty, user.getId(), cust.getId());
                // Показываем обстановку
                setSituation(NetCommander.getSelfServices(netProperty, user.getId()));
                // поддержка расширяемости плагинами
                extPluginIStartClientPressedButton(user, netProperty, getUserPlan(), new ActionEvent(menuItemInvitePostponed, 1, KEYS_MAY_INVITE), 1);
                end(start);
            } else {
                JOptionPane.showMessageDialog(null, getLocaleMessage("postponed.already_invited"), getLocaleMessage("invite.posponed.title"), JOptionPane.INFORMATION_MESSAGE);
            }
        }
    }

    /**
     * Изменим статус отложенному.
     */
    @Action
    public void changeStatusForPosponed() {
        if (listPostponed.getSelectedIndex() != -1) {
            final long start = go();
            final QCustomer cust = (QCustomer) listPostponed.getSelectedValue();

            String status = (String) JOptionPane.showInputDialog(this, getLocaleMessage("resultwork.dialog.caption"), getLocaleMessage("resultwork.dialog.title"),
                    JOptionPane.QUESTION_MESSAGE, null, getResults(), null);
            if (status == null) {
                return;
            }
            try {
                NetCommander.postponeCustomerChangeStatus(netProperty, cust.getId(), status);
            } catch (QException e) {
                throw new ClientException(e);
            }
            // Показываем обстановку, должно быть оповешение по udp
            end(start);
        }
    }

    @Action
    public void manageFlexPriority() {
        FServicePriority.show(netProperty, this, getUserPlan(), user.getId());
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    // Generated using JFormDesigner non-commercial license
    public JMenuBar clientMenuBar;
    public JMenu fileMenu;
    private JMenu menuLangs;
    private JMenuItem menuItemRefresh;
    private JMenuItem exitMenuItem;
    private JMenu optionsMenu;
    private JMenuItem menuItemFlexPriority;
    private JMenuItem menuItemId;
    private JMenu editMenu;
    private JMenuItem menuItemInvite;
    private JMenuItem menuItemKill;
    private JMenuItem menuItemStart;
    private JMenuItem menuItemRedirect;
    private JMenuItem menuItemMoveToPostponed;
    private JMenuItem menuItemFinish;
    private JMenu helpMenu;
    private JMenuItem menuItemHelp;
    private JMenuItem aboutMenuItem;
    private JPanel panelDown;
    private JPanel jPanel4;
    private JLabel jLabel2;
    private JSplitPane jSplitPane1;
    private JPanel panelBottom;
    private JScrollPane jScrollPane5;
    private JTextArea textAreaComments;
    public JButton btnPushToTalk;
    private JPanel jPanel1;
    private JPanel panelButtondBlock;
    private JButton buttonInvite;
    private JButton buttonKill;
    private JButton buttonStart;
    private JButton buttonFinish;
    private JLabel jLabel1;
    private JButton buttonRedirect;
    private JButton buttonMoveToPostponed;
    private JScrollPane jScrollPane2;
    public JTextPane labelNextNumber;
    private JPanel panelWorkplace;
    private JLabel jLabel3;
    private JLabel labelUser;
    private JLabel jLabel4;
    private JLabel labelResume;
    private JTabbedPane jTabbedPane1;
    private JPanel jPanel5;
    private JScrollPane jScrollPane6;
    public JTree treeSituation;
    private JPanel jPanel6;
    private JScrollPane jScrollPane3;
    private JLabel labelSituationAll;
    private JPanel jPanel7;
    private JScrollPane jScrollPane1;
    private JLabel labelMessage;
    private JLabel jLabel5;
    private JLabel labelPost;
    private JScrollPane jScrollPane4;
    private JList<QCustomer> listPostponed;
    private JPanel panelParallerCusts;
    private JLabel labelPaallelcaption;
    private JScrollPane jScrollPane7;
    private JList<QCustomer> listParallelClients;
    private JPopupMenu popupMenuPostpone;
    private JMenuItem menuItemInvitePostponed;
    private JMenuItem menuItemChangeStatusPostponed;
    private JPopupMenu popupClients;
    private JMenuItem miFlexInvitation;
    private InviteNextAction inviteNextAction;
    private KillCustomerAction killCustomerAction;
    private StartWorkCustomerAction startWorkCustomerAction;
    private RedirectCustomerAction redirectCustomerAction;
    private MoveToPostponedAction moveToPostponedAction;
    private StopWorkCustomerAction stopWorkCustomerAction;
    private AboutAction aboutAction;
    private RefreshAction refreshAction;
    private ExitAction actionExit;
    private HelpAction helpAction;
    private InvitePostponedAction invitePostponedAction;
    private PostponeChangeStatusAction postponeChangeStatusAction;
    private PriorityAction priorityAction;
    // End of variables declaration//GEN-END:variables

    private class InviteNextAction extends AbstractAction {
        private InviteNextAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.event.invite"));
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/ru/apertum/qsystem/client/forms/resources/Refresh.png")));
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_DOWN_MASK));
            putValue(SHORT_DESCRIPTION, bundle.getString("client.event.invite.tip"));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            inviteNextCustomer(e);
        }
    }

    private class KillCustomerAction extends AbstractAction {
        private KillCustomerAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.killCustomerAction.Name"));
            putValue(SHORT_DESCRIPTION, bundle.getString("client.killCustomerAction.ShortDescription"));
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/ru/apertum/qsystem/client/forms/resources/Delete.png")));
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            killCustomer(e);
        }
    }

    private class StartWorkCustomerAction extends AbstractAction {
        private StartWorkCustomerAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.event.start"));
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/ru/apertum/qsystem/client/forms/resources/Undo.png")));
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK));
            putValue(SHORT_DESCRIPTION, bundle.getString("client.event.start.tip"));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            getStartCustomer(e);
        }
    }

    private class RedirectCustomerAction extends AbstractAction {
        private RedirectCustomerAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.event.redirect"));
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/ru/apertum/qsystem/client/forms/resources/redirect.png")));
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_DOWN_MASK));
            putValue(SHORT_DESCRIPTION, bundle.getString("client.event.redirect.tip"));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            redirectCustomer(e);
        }
    }

    private class MoveToPostponedAction extends AbstractAction {
        private MoveToPostponedAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.event.postpone"));
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/ru/apertum/qsystem/client/forms/resources/postpon.png")));
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK));
            putValue(SHORT_DESCRIPTION, bundle.getString("client.event.postpone.tip"));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            moveToPostponed(e);
        }
    }

    private class StopWorkCustomerAction extends AbstractAction {
        private StopWorkCustomerAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.event.finish"));
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/ru/apertum/qsystem/client/forms/resources/Check.png")));
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK));
            putValue(SHORT_DESCRIPTION, bundle.getString("client.event.finish.tip"));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            getStopCustomer(e);
        }
    }

    private class AboutAction extends AbstractAction {
        private AboutAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.menu.about"));
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_DOWN_MASK));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            showAboutBox();
        }
    }

    private class RefreshAction extends AbstractAction {
        private RefreshAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.event.refresh"));
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            refreshClient();
        }
    }

    private class ExitAction extends AbstractAction {
        private ExitAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.event.exit"));
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_DOWN_MASK));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            exitMenuItemActionPerformed(e);
        }
    }

    private class HelpAction extends AbstractAction {
        private HelpAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.helpAction.Name"));
            putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            help();
        }
    }

    private class InvitePostponedAction extends AbstractAction {
        private InvitePostponedAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.event.invite-postponed"));
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/ru/apertum/qsystem/client/forms/resources/redo.png")));
            putValue(SHORT_DESCRIPTION, bundle.getString("client.event.invite-postponed.tip"));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        @SneakyThrows
        public void actionPerformed(ActionEvent e) {
            invitePostponed();
        }
    }

    private class PostponeChangeStatusAction extends AbstractAction {
        private PostponeChangeStatusAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.event.change-stutas-postponed"));
            putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/ru/apertum/qsystem/client/forms/resources/refresh2.png")));
            putValue(SHORT_DESCRIPTION, bundle.getString("client.event.change-stutas-postponed.tip"));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            changeStatusForPosponed();
        }
    }

    private class PriorityAction extends AbstractAction {
        private PriorityAction() {
            // JFormDesigner - Action initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
            // Generated using JFormDesigner non-commercial license
            ResourceBundle bundle = ResourceBundle.getBundle("ru.apertum.qsystem.common.resources.locale");

            putValue(NAME, bundle.getString("client.priorityAction.Name"));
            // JFormDesigner - End of action initialization  //GEN-END:initComponents
        }

        public void actionPerformed(ActionEvent e) {
            manageFlexPriority();
        }
    }
}
